package com.doctcloud.datacloud.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.doctcloud.common.core.domain.R;
import com.doctcloud.common.core.utils.bean.BeanUtils;
import com.doctcloud.datacloud.entity.OutpRcptMasterEntity;
import com.doctcloud.datacloud.entity.PatMasterIndexEntity;
import com.doctcloud.datacloud.mapper.OutpRcptMasterEntityMapper;
import com.doctcloud.datacloud.mapper.PatMasterIndexEntityMapper;
import com.doctcloud.hisinfo.api.RemoteHisMedrecService;
import com.doctcloud.hisinfo.api.RemoteHisOutpBillService;
import com.doctcloud.hisinfo.histable.domain.outpbill.OutpRcptMasterDo;
import com.doctcloud.hisinfo.histable.domain.medrec.PatMasterIndexDo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.doctcloud.common.core.utils.DateUtils.formatSimpDateWithTime;

@Slf4j
@Service
@RequiredArgsConstructor
public class OutpRcptMasterEntityService extends ServiceImpl<OutpRcptMasterEntityMapper, OutpRcptMasterEntity> {

    @Value("${sync.batch.size:1000}")
    private int BATCH_SIZE;

    // 新增：远程查询分批大小。这个值可以根据远程服务的承受能力调整，例如 100
    @Value("${sync.remote.query.batch.size:100}")
    private int REMOTE_QUERY_BATCH_SIZE;

    private final RemoteHisOutpBillService remoteHisOutpBillService;
    private final RemoteHisMedrecService remoteHisMedrecService;
    private final PatMasterIndexEntityMapper patMasterIndexEntityMapper;

    /**
     * 同步指定时间段内的门诊收费主记录，并确保关联的患者主索引信息也同步。
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncRcptMaster(Date startDate, Date endDate) {
        // 参数校验
        if (startDate == null || endDate == null) {
            log.error("同步门诊收费主记录失败：起始时间或截止时间不能为空");
            throw new IllegalArgumentException("同步失败：起始时间和截止时间均不能为空");
        }
        if (startDate.after(endDate)) {
            log.error("同步门诊收费主记录失败：起始时间不能晚于截止时间");
            throw new IllegalArgumentException("同步失败：起始时间不能晚于截止时间");
        }

        String startDateStr = formatSimpDateWithTime(startDate);
        String endDateStr = formatSimpDateWithTime(endDate);
        log.info("--- 开始同步门诊收费主记录：起始时间={}, 截止时间={} ---", startDateStr, endDateStr);
        long startTime = System.currentTimeMillis();

        try {
            // 1. 从远程服务获取门诊收费主记录
            log.info("正在从远程服务获取门诊收费主记录...");
            List<OutpRcptMasterDo> remoteOutpRcpts = remoteHisOutpBillService.listRcptByDate(startDate, endDate);
            if (CollectionUtils.isEmpty(remoteOutpRcpts)) {
                log.warn("在时间段 [{} 至 {}] 内，从远程服务获取的门诊收费主记录为空，同步任务终止。", startDateStr, endDateStr);
                return;
            }
            log.info("从远程服务获取到 {} 条门诊收费主记录。", remoteOutpRcpts.size());

            // 2. 提取并同步关联的患者主索引信息 (核心优化点)
            this.syncAssociatedPatMasterIndexes(remoteOutpRcpts);

            // 3. 转换、过滤门诊收费主记录
            List<OutpRcptMasterEntity> entitiesToUpsert = remoteOutpRcpts.stream()
                    .filter(this::isValidOutpRcptMasterDo)
                    .map(this::convertDoToEntity)
                    .collect(Collectors.toList());

            if (CollectionUtils.isEmpty(entitiesToUpsert)) {
                log.info("在时间段 [{} 至 {}] 内，没有需要同步的有效门诊收费主记录。", startDateStr, endDateStr);
                return;
            }
            log.info("准备执行门诊收费主记录批量UPSERT操作，共 {} 条记录，分批大小：{}。", entitiesToUpsert.size(), BATCH_SIZE);

            // 4. 分批执行UPSERT
            int totalAffectedRows = this.executeBatchUpsert(entitiesToUpsert);

            log.info("--- 同步完成。时间段 [{} 至 {}]。总耗时：{} ms。门诊收费主记录影响行数: {} ---",
                    startDateStr, endDateStr, System.currentTimeMillis() - startTime, totalAffectedRows);

        } catch (IllegalArgumentException e) {
            log.error("同步门诊收费主记录失败：{}", e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("同步门诊收费主记录时发生异常: ", e);
            throw new RuntimeException("同步门诊收费主记录失败：" + e.getMessage(), e);
        }
    }

    /**
     * 同步与收费记录关联的患者主索引信息（最终优化版）。
     * 增加了多重防御，确保不会因 patient_id 为 null 而导致数据库插入失败。
     */
    private void syncAssociatedPatMasterIndexes(List<OutpRcptMasterDo> remoteOutpRcpts) {
        // 2.1 提取所有不重复的、有效的患者ID
        List<String> allPatientIds = remoteOutpRcpts.stream()
                .map(OutpRcptMasterDo::getPatientId)
                .filter(Objects::nonNull)
                .filter(id -> !id.trim().isEmpty())
                .distinct()
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(allPatientIds)) {
            log.info("没有需要同步的患者ID。");
            return;
        }
        log.info("共涉及 {} 个不重复的患者ID，开始同步患者主索引信息...", allPatientIds.size());

        // 2.2 批量查询本地已存在的患者
        LambdaQueryWrapper<PatMasterIndexEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(PatMasterIndexEntity::getPatientId, allPatientIds);
        List<PatMasterIndexEntity> existingPatients = patMasterIndexEntityMapper.selectList(queryWrapper);
        Map<String, PatMasterIndexEntity> existingPatientMap = existingPatients.stream()
                .collect(Collectors.toMap(PatMasterIndexEntity::getPatientId, p -> p));

        // 2.3 计算需要从远程获取的患者ID (本地不存在的)
        List<String> patientIdsToFetch = allPatientIds.stream()
                .filter(patientId -> !existingPatientMap.containsKey(patientId))
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(patientIdsToFetch)) {
            log.info("所有相关患者主索引信息在本地均已存在，无需从远程获取。");
            return;
        }
        log.info("本地缺少 {} 个患者主索引信息，将从远程分批获取...", patientIdsToFetch.size());

        Date currentDate = new Date();
        List<PatMasterIndexEntity> bufferForInsert = new ArrayList<>();

        for (int i = 0; i < patientIdsToFetch.size(); i += REMOTE_QUERY_BATCH_SIZE) {
            int end = Math.min(i + REMOTE_QUERY_BATCH_SIZE, patientIdsToFetch.size());
            List<String> batchIds = patientIdsToFetch.subList(i, end);

            log.debug("正在处理第 {} 批患者信息查询，批次大小: {}", (i / REMOTE_QUERY_BATCH_SIZE) + 1, batchIds.size());

            for (String patientId : batchIds) {
                // 即使 batchIds 来自 patientIdsToFetch，也做一次最后的非空校验，以防万一
                if (patientId == null || patientId.trim().isEmpty()) {
                    log.warn("发现一个无效的 patientId，将跳过。ID: {}", patientId);
                    continue;
                }

                try {
                    R<PatMasterIndexDo> response = remoteHisMedrecService.getPatMasterIndexById(patientId);
                    if (response != null && R.isSuccess(response)) {
                        PatMasterIndexDo patMasterIndexDo = response.getData();

                        // 【核心防御】检查从远程获取的Do对象本身及其patientId是否有效
                        if (patMasterIndexDo == null) {
                            log.warn("远程服务为患者 [{}] 返回的数据为空 (PatMasterIndexDo is null)。", patientId);
                            continue;
                        }
                        if (patMasterIndexDo.getPatientId() == null || patMasterIndexDo.getPatientId().trim().isEmpty()) {
                            log.error("远程服务返回的患者信息中，patient_id 为 null 或空！请求的 patientId: {}", patientId);
                            log.error("返回的完整 PatMasterIndexDo 对象: {}", patMasterIndexDo); // 打印完整对象，方便排查
                            continue;
                        }

                        PatMasterIndexEntity entity = new PatMasterIndexEntity();
                        BeanUtils.copyProperties(patMasterIndexDo, entity);
                        entity.setUpdateDate(currentDate);

                        // 【最终校验】在添加到缓冲区前，再次确认 entity 的 patientId 有效
                        if (isNotEmpty(entity.getPatientId())) {
                            bufferForInsert.add(entity);
                        } else {
                            // 这一步理论上不应该执行到，如果执行到了，说明有极其特殊的情况
                            log.error("患者信息转换为 Entity 后，patient_id 变为 null！原始请求的 patientId: {}", patientId);
                            continue;
                        }

                        if (bufferForInsert.size() >= BATCH_SIZE) {
                            log.debug("患者信息缓冲区已满，执行批量插入 {} 条记录。", bufferForInsert.size());
                            patMasterIndexEntityMapper.batchInsert(bufferForInsert);
                            bufferForInsert.clear();
                        }
                    } else {
                        String errorMsg = (response != null) ? response.getMsg() : "响应为空或请求失败";
                        log.warn("获取患者 [{}] 信息失败: {}", patientId, errorMsg);
                    }
                } catch (Exception e) {
                    log.error("获取患者 [{}] 信息时发生异常，将跳过该患者。", patientId, e);
                }
            }
        }

        // 处理缓冲区中剩余的数据
        if (!bufferForInsert.isEmpty()) {
            log.debug("同步结束，处理缓冲区中剩余的 {} 条患者记录。", bufferForInsert.size());
            patMasterIndexEntityMapper.batchInsert(bufferForInsert);
            bufferForInsert.clear();
        }

        log.info("患者主索引信息同步完成。");
    }

    /**
     * 执行分批 UPSERT 操作
     */
    private int executeBatchUpsert(List<OutpRcptMasterEntity> entitiesToUpsert) {
        int totalAffectedRows = 0;
        int totalBatches = (int) Math.ceil((double) entitiesToUpsert.size() / BATCH_SIZE);

        for (int i = 0; i < totalBatches; i++) {
            int startIndex = i * BATCH_SIZE;
            int endIndex = Math.min(startIndex + BATCH_SIZE, entitiesToUpsert.size());
            List<OutpRcptMasterEntity> batchEntities = entitiesToUpsert.subList(startIndex, endIndex);
            log.debug("正在执行第 {} 批/共 {} 批门诊收费主记录UPSERT，处理 {} 条记录。", i + 1, totalBatches, batchEntities.size());
            int affectedRows = baseMapper.batchUpsert(batchEntities);
            totalAffectedRows += affectedRows;
        }
        return totalAffectedRows;
    }

    /**
     * 校验 OutpRcptMasterDo 对象是否有效。
     */
    private boolean isValidOutpRcptMasterDo(OutpRcptMasterDo doObj) {
        return doObj != null && isNotEmpty(doObj.getPatientId()) && isNotEmpty(doObj.getRcptNo());
    }

    /**
     * 将 OutpRcptMasterDo 转换为 OutpRcptMasterEntity。
     */
    private OutpRcptMasterEntity convertDoToEntity(OutpRcptMasterDo doObj) {
        OutpRcptMasterEntity entity = new OutpRcptMasterEntity();
        BeanUtils.copyProperties(doObj, entity);
        return entity;
    }

    /**
     * 通用判空：判断字符串是否非空。
     */
    private boolean isNotEmpty(String str) {
        return str != null && !str.trim().isEmpty();
    }
}